/* **********************************************************************************
*
* Copyright (c) Microsoft Corporation. All rights reserved.
*
* **********************************************************************************/

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Text;
using System.Windows.Forms;
using System.Collections;
using System.Threading;
using OfficeOpenXMLInterop;

namespace Theme_Builder
{
    // Panel that doesn't draw it's background
    public class PanelNoBackground : System.Windows.Forms.Panel
    {
        protected override void OnPaintBackground(System.Windows.Forms.PaintEventArgs e)
        {
            // Do nothing
        }
    }

    public partial class GradientEditor : UserControl
    {
        private ControlSyncTrackBarNumericUpDown controlSyncStopPos;

        private Color placeholderColor;

        public GradientEditor()
        {
            InitializeComponent();

            this.controlSyncStopPos = new ControlSyncTrackBarNumericUpDown(this.trackBarStopPos, this.numericUpDownStopPos);

            // Create the first default gradient stop
            CT_GradientStop stop = new OfficeOpenXMLInterop.CT_GradientStop();
            stop.pos = 0;
            stop.schemeClr = new OfficeOpenXMLInterop.CT_SchemeColor();
            stop.schemeClr.val = OfficeOpenXMLInterop.ST_SchemeColorVal.phClr;
            GradientStop gs = new GradientStop();
            gs.Number = 1;
            gs.Stop = stop;
            this.comboBoxGradStops.Items.Add(gs);
            
            // Create a second default gradient stop
            stop = new OfficeOpenXMLInterop.CT_GradientStop();
            stop.pos = 100000;
            stop.schemeClr = new OfficeOpenXMLInterop.CT_SchemeColor();
            stop.schemeClr.val = OfficeOpenXMLInterop.ST_SchemeColorVal.phClr;
            gs = new GradientStop();
            gs.Number = 2;
            gs.Stop = stop;
            this.comboBoxGradStops.Items.Add(gs);

            // Select the first one in the list
            this.comboBoxGradStops.SelectedIndex = 0;

            // Linear gradient by default
            this.comboBoxGradType.SelectedIndex = 0;

            this.placeholderColor = Color.Fuchsia;      // horribly ugly default color that should be updated by controls that contain this
        }

        public OfficeOpenXMLInterop.CT_GradientFillProperties GradientFill
        {
            get
            {
                return this.GetGradFill();
            }
            set
            {
                this.SetGradFill(value);
            }
        }

        /// <summary>
        /// Retrieves a CT_GradientFillProperties containing a definition for a gradient fill
        /// </summary>
        /// <returns>The gradient fill</returns>
        private OfficeOpenXMLInterop.CT_GradientFillProperties GetGradFill()
        {
            OfficeOpenXMLInterop.CT_GradientFillProperties gradFill = new OfficeOpenXMLInterop.CT_GradientFillProperties();
            gradFill.gsLst = new OfficeOpenXMLInterop.CT_GradientStop[this.comboBoxGradStops.Items.Count];

            for (int i = 0; i < this.comboBoxGradStops.Items.Count; i++)
            {
                GradientStop stop = (GradientStop)this.comboBoxGradStops.Items[i];
                gradFill.gsLst[i] = stop.Stop;
            }

            gradFill.rotWithShape = this.checkBoxRotate.Checked;

            if (this.comboBoxGradType.Text == "Linear")
            {
                gradFill.lin = new OfficeOpenXMLInterop.CT_LinearShadeProperties();
                gradFill.lin.ang = (int)Math.Round(this.numericUpDownAngle.Value * 60000);
                gradFill.lin.angSpecified = true;
                gradFill.lin.scaled = this.checkBoxScaled.Checked;
                gradFill.lin.scaledSpecified = true;
            }
            else if (this.comboBoxGradType.Text == "Radial")
            {
                gradFill.path = new OfficeOpenXMLInterop.CT_PathShadeProperties();
                gradFill.path.path = OfficeOpenXMLInterop.ST_PathShadeType.circle;
                gradFill.path.pathSpecified = true;
                gradFill.path.fillToRect = new OfficeOpenXMLInterop.CT_RelativeRect();
                gradFill.path.fillToRect.b = (int)Math.Round(Double.Parse(this.textBoxBottomRect.Text) * 1000);
                gradFill.path.fillToRect.r = (int)Math.Round(Double.Parse(this.textBoxRightRect.Text) * 1000);
                gradFill.path.fillToRect.t = (int)Math.Round(Double.Parse(this.textBoxTopRect.Text) * 1000);
                gradFill.path.fillToRect.l = (int)Math.Round(Double.Parse(this.textBoxLeftRect.Text) * 1000);
            }
            else if (this.comboBoxGradType.Text == "Rectangular")
            {
                gradFill.path = new OfficeOpenXMLInterop.CT_PathShadeProperties();
                gradFill.path.path = OfficeOpenXMLInterop.ST_PathShadeType.rect;
                gradFill.path.pathSpecified = true;
                gradFill.path.fillToRect = new OfficeOpenXMLInterop.CT_RelativeRect();
                gradFill.path.fillToRect.b = (int)Math.Round(Double.Parse(this.textBoxBottomRect.Text) * 1000);
                gradFill.path.fillToRect.r = (int)Math.Round(Double.Parse(this.textBoxRightRect.Text) * 1000);
                gradFill.path.fillToRect.t = (int)Math.Round(Double.Parse(this.textBoxTopRect.Text) * 1000);
                gradFill.path.fillToRect.l = (int)Math.Round(Double.Parse(this.textBoxLeftRect.Text) * 1000);
            }

            return gradFill;
        }

        /// <summary>
        /// Sets the gradient fill properties of this control
        /// </summary>
        /// <param name="gradFill">CT_GradientFillProperties containing the desired settings</param>
        private void SetGradFill(OfficeOpenXMLInterop.CT_GradientFillProperties gradFill)
        {
            if (gradFill == null || gradFill.gsLst == null || gradFill.gsLst.Length < 2)
                return;

            this.buttonRemoveStop.Enabled = false;
            this.groupBoxLinearOptions.Visible = false;
            this.groupBoxPathOptions.Visible = false;
            
            this.comboBoxGradStops.Items.Clear();

            // Handle the gradient stops
            for (int i = 0; i < gradFill.gsLst.Length; i++)
            {
                GradientStop gs = new GradientStop();
                gs.Number = i + 1;
                gs.Stop = gradFill.gsLst[i];
                this.comboBoxGradStops.Items.Add(gs);
            }

            this.checkBoxRotate.Checked = gradFill.rotWithShape;

            if (gradFill.lin != null)
            {
                this.comboBoxGradType.SelectedIndex = 0;
                this.groupBoxLinearOptions.Visible = true;

                if (gradFill.lin.scaledSpecified)
                    this.checkBoxScaled.Checked = gradFill.lin.scaled;
                if (gradFill.lin.angSpecified)
                    this.numericUpDownAngle.Value = (decimal)(gradFill.lin.ang / 60000m);
            }
            else if (gradFill.path != null)
            {
                if (gradFill.path.path == OfficeOpenXMLInterop.ST_PathShadeType.circle)
                {
                    this.comboBoxGradType.SelectedIndex = 1;
                    this.groupBoxPathOptions.Visible = true;
                    this.groupBoxPathOptions.Text = "Radial Gradient Options";
                    this.textBoxBottomRect.Text = (gradFill.path.fillToRect.b / 1000.0f).ToString();
                    this.textBoxTopRect.Text = (gradFill.path.fillToRect.t / 1000.0f).ToString();
                    this.textBoxLeftRect.Text = (gradFill.path.fillToRect.l / 1000.0f).ToString();
                    this.textBoxRightRect.Text = (gradFill.path.fillToRect.r / 1000.0f).ToString();
                }
                else if (gradFill.path.path == OfficeOpenXMLInterop.ST_PathShadeType.rect)
                {
                    this.comboBoxGradType.SelectedIndex = 2;
                    this.groupBoxPathOptions.Visible = true;
                    this.groupBoxPathOptions.Text = "Rectangular Gradient Options";
                    this.textBoxBottomRect.Text = (gradFill.path.fillToRect.b / 1000.0f).ToString();
                    this.textBoxTopRect.Text = (gradFill.path.fillToRect.t / 1000.0f).ToString();
                    this.textBoxLeftRect.Text = (gradFill.path.fillToRect.l / 1000.0f).ToString();
                    this.textBoxRightRect.Text = (gradFill.path.fillToRect.r / 1000.0f).ToString();
                }
                else if (gradFill.path.path == OfficeOpenXMLInterop.ST_PathShadeType.shape)
                {
                    // TODO: Figure out how to handle a gradient path
                    System.Diagnostics.Debug.Assert(false, "Not Yet Implemented");
                }
            }

            this.UpdateSelectedColor();
            this.UpdateGradientPreview();

            if (this.comboBoxGradStops.Items.Count > 2)
            {
                this.buttonRemoveStop.Enabled = true;
            }

            this.comboBoxGradStops.SelectedIndex = 0;
        }

        /// <summary>
        /// Used to set the placeholder color for use in displaying the end gradient
        /// </summary>
        public Color PlaceholderColor
        {
            set
            {
                this.placeholderColor = value;
                this.UpdateSelectedColor();
                this.UpdateGradientPreview();
            }
        }

        private void UpdateSelectedColor()
        {
            this.panelColor.Invalidate();
        }

        private ColorMods GetColorModifications(GradientStop gs)
        {
            if (gs.Stop.schemeClr != null && gs.Stop.schemeClr.val == OfficeOpenXMLInterop.ST_SchemeColorVal.phClr)
            {
                return Converters.GetColorModifications(gs.Stop.schemeClr);
            }
            else
            {
                // TODO: Figure out how to effectively handle the other color types besides scheme colors
                System.Diagnostics.Debug.Assert(false, "Not Yet Implemented");
            }

            ColorMods mods = new ColorMods();
            return mods;
        }

        private Color GetColorFromStop(GradientStop gs)
        {
            Color color = Color.White;
            ColorMods mods = new ColorMods();

            if (gs.Stop.schemeClr != null && gs.Stop.schemeClr.val == OfficeOpenXMLInterop.ST_SchemeColorVal.phClr)
            {
                if (gs.Stop.schemeClr.alpha != null)
                    mods.alpha = gs.Stop.schemeClr.alpha.val / 1000;
                if (gs.Stop.schemeClr.tint != null)
                    mods.tint = gs.Stop.schemeClr.tint.val / 1000;
                if (gs.Stop.schemeClr.shade != null)
                    mods.shade = gs.Stop.schemeClr.shade.val / 1000;
                if (gs.Stop.schemeClr.hueMod != null)
                    mods.hueMod = gs.Stop.schemeClr.hueMod.val / 1000;
                if (gs.Stop.schemeClr.satMod != null)
                    mods.satMod = gs.Stop.schemeClr.satMod.val / 1000;
                if (gs.Stop.schemeClr.lumMod != null)
                    mods.lumMod = gs.Stop.schemeClr.lumMod.val / 1000;
                if (gs.Stop.schemeClr.greenMod != null)
                    mods.greenMod = gs.Stop.schemeClr.greenMod.val / 1000;
                if (gs.Stop.schemeClr.redMod != null)
                    mods.redMod = gs.Stop.schemeClr.redMod.val / 1000;
                if (gs.Stop.schemeClr.blueMod != null)
                    mods.blueMod = gs.Stop.schemeClr.blueMod.val / 1000;

                color = this.placeholderColor;
            }
            else
            {
                // TODO: Figure out how to effectively handle the other colors
                System.Diagnostics.Debug.Assert(false, "Not Yet Implemented");
            }

            return Converters.ModifyColor(color, mods);
        }

        private void UpdateGradientPreview()
        {
            this.panelGradientPreview.Invalidate();
        }

        private void comboBoxGradType_SelectedIndexChanged(object sender, EventArgs e)
        {
            // Hide all the stuff that coule be visible
            this.groupBoxLinearOptions.Visible = false;
            this.groupBoxPathOptions.Visible = false;

            // Show the activated item's UI
            switch (this.comboBoxGradType.SelectedIndex)
            {
                case 0: // Linear
                    this.groupBoxLinearOptions.Visible = true;
                    break;
                case 1: // Radial
                    this.groupBoxPathOptions.Visible = true;
                    this.groupBoxPathOptions.Text = "Radial Gradient Options";
                    break;
                case 2: // Rectangular
                    this.groupBoxPathOptions.Visible = true;
                    this.groupBoxPathOptions.Text = "Rectangular Gradient Options";
                    break;
                case 3:
                    break;
            }
        }

        private void trackBarStopPos_Scroll(object sender, EventArgs e)
        {
            // Sync the two controls around this action
            this.controlSyncStopPos.SyncControls((decimal)this.trackBarStopPos.Value);
        }

        private void numericUpDownStopPos_ValueChanged(object sender, EventArgs e)
        {
            // Sync the two controls around this action
            this.controlSyncStopPos.SyncControls(this.numericUpDownStopPos.Value);

            // Update the gradient stop
            lock (this.comboBoxGradStops)
            {
                // Update the UI for the selected stop
                GradientStop gs = (GradientStop)this.comboBoxGradStops.SelectedItem;
                gs.Stop.pos = (int)(this.numericUpDownStopPos.Value * 1000);
            }

            this.UpdateGradientPreview();
        }

        private void comboBoxGradStops_SelectedIndexChanged(object sender, EventArgs e)
        {
            lock (this.comboBoxGradStops)
            {
                // Update the UI for the selected stop
                GradientStop gs = (GradientStop)this.comboBoxGradStops.SelectedItem;

                // Set the position of the stop
                this.numericUpDownStopPos.Value = (decimal)((decimal)gs.Stop.pos / (decimal)1000);
            }

            this.UpdateSelectedColor();
        }

        private void buttonAddStop_Click(object sender, EventArgs e)
        {
            lock (this.comboBoxGradStops)
            {
                // Get how many we currently have
                int count = this.comboBoxGradStops.Items.Count;

                // Set the new stop to be equal in everything to the last stop
                CT_GradientStop lastStop = ((GradientStop)this.comboBoxGradStops.Items[count - 1]).Stop;
                CT_GradientStop stop = new OfficeOpenXMLInterop.CT_GradientStop();

                // TODO: Add the other types here if they ever get implemented
                if (lastStop.schemeClr != null)
                {
                    stop.schemeClr = new OfficeOpenXMLInterop.CT_SchemeColor();
                    stop.schemeClr.val = lastStop.schemeClr.val;
                    if (lastStop.schemeClr.alpha != null)
                    {
                        stop.schemeClr.alpha = new OfficeOpenXMLInterop.CT_PositiveFixedPercentage();
                        stop.schemeClr.alpha.val = lastStop.schemeClr.alpha.val;
                    }
                    if (lastStop.schemeClr.tint != null)
                    {
                        stop.schemeClr.tint = new OfficeOpenXMLInterop.CT_PositiveFixedPercentage();
                        stop.schemeClr.tint.val = lastStop.schemeClr.tint.val;
                    }
                    if (lastStop.schemeClr.shade != null)
                    {
                        stop.schemeClr.shade = new OfficeOpenXMLInterop.CT_PositiveFixedPercentage();
                        stop.schemeClr.shade.val = lastStop.schemeClr.shade.val;
                    }
                    if (lastStop.schemeClr.hueMod != null)
                    {
                        stop.schemeClr.hueMod = new OfficeOpenXMLInterop.CT_PositivePercentage();
                        stop.schemeClr.hueMod.val = lastStop.schemeClr.hueMod.val;
                    }
                    if (lastStop.schemeClr.satMod != null)
                    {
                        stop.schemeClr.satMod = new OfficeOpenXMLInterop.CT_Percentage();
                        stop.schemeClr.satMod.val = lastStop.schemeClr.satMod.val;
                    }
                    if (lastStop.schemeClr.lumMod != null)
                    {
                        stop.schemeClr.lumMod = new OfficeOpenXMLInterop.CT_Percentage();
                        stop.schemeClr.lumMod.val = lastStop.schemeClr.lumMod.val;
                    }
                    if (lastStop.schemeClr.greenMod != null)
                    {
                        stop.schemeClr.greenMod = new OfficeOpenXMLInterop.CT_Percentage();
                        stop.schemeClr.greenMod.val = lastStop.schemeClr.greenMod.val;
                    }
                    if (lastStop.schemeClr.redMod != null)
                    {
                        stop.schemeClr.redMod = new OfficeOpenXMLInterop.CT_Percentage();
                        stop.schemeClr.redMod.val = lastStop.schemeClr.redMod.val;
                    }
                    if (lastStop.schemeClr.blueMod != null)
                    {
                        stop.schemeClr.blueMod = new OfficeOpenXMLInterop.CT_Percentage();
                        stop.schemeClr.blueMod.val = lastStop.schemeClr.blueMod.val;
                    }
                }

                stop.pos = 100000;

                GradientStop gs = new GradientStop();
                gs.Number = count + 1;
                gs.Stop = stop;

                this.comboBoxGradStops.Items.Add(gs);

                // Enable the remove button if it's disabled
                if (!this.buttonRemoveStop.Enabled)
                    this.buttonRemoveStop.Enabled = true;
            }

            this.UpdateGradientPreview();
        }

        private void buttonRemoveStop_Click(object sender, EventArgs e)
        {
            lock (this.comboBoxGradStops)
            {
                int selectedPos = this.comboBoxGradStops.SelectedIndex;
                int count = this.comboBoxGradStops.Items.Count;

                // Decrement all the following stop numbers
                for (int i = selectedPos + 1; i < count; i++)
                {
                    ((GradientStop)this.comboBoxGradStops.Items[i]).Number--;
                }

                // Remove the selected stop
                this.comboBoxGradStops.Items.RemoveAt(selectedPos);

                // If there are only two stops left, then we need to disable this button
                if (this.buttonRemoveStop.Enabled && this.comboBoxGradStops.Items.Count <= 2)
                    this.buttonRemoveStop.Enabled = false;

                // Force the combobox to recreate itself so that we get updated information showing
                //  in the combobox.  
                // TODO: This is a bad way to do this, we should create a new data type
                //  that implements IBindingList which would enable the combobox to know when an
                //  item is actually updated.
                object[] collection = new object[this.comboBoxGradStops.Items.Count];
                this.comboBoxGradStops.Items.CopyTo(collection, 0);

                this.comboBoxGradStops.Items.Clear();

                foreach (object obj in collection)
                {
                    this.comboBoxGradStops.Items.Add(obj);
                }

                // Reset the selected item, if we're the last item, set it to the last item
                if (selectedPos < this.comboBoxGradStops.Items.Count)
                    this.comboBoxGradStops.SelectedIndex = selectedPos;
                else
                    this.comboBoxGradStops.SelectedIndex = selectedPos - 1;
            }

            this.UpdateGradientPreview();
        }

        private void panelColor_Click(object sender, EventArgs e)
        {
            ColorPickerExpanded cpe = new ColorPickerExpanded();

            GradientStop gs = (GradientStop)this.comboBoxGradStops.SelectedItem;

            if(gs == null)
                return;

            ColorMods mods = this.GetColorModifications(gs);

            cpe.ColorModifications = mods;

            if (cpe.ShowDialog(this) == DialogResult.OK)
            {
                // TODO: This should be refactored to use common color mod parsing, and ensure works with floating point
                // TODO: Adjust this when we can do more than simply a placeholder color
                ColorMods updatedMods = cpe.ColorModifications;

                if(updatedMods.alpha != 100 || mods.alpha != 100)
                {
                    gs.Stop.schemeClr.alpha = new OfficeOpenXMLInterop.CT_PositiveFixedPercentage();
                    gs.Stop.schemeClr.alpha.val = (int)updatedMods.alpha * 1000;
                }
                if (updatedMods.tint != 100 || mods.tint != 100)
                {
                    gs.Stop.schemeClr.tint = new OfficeOpenXMLInterop.CT_PositiveFixedPercentage();
                    gs.Stop.schemeClr.tint.val = (int)updatedMods.tint * 1000;
                }
                if (updatedMods.shade != 100 || mods.shade != 100)
                {
                    gs.Stop.schemeClr.shade = new OfficeOpenXMLInterop.CT_PositiveFixedPercentage();
                    gs.Stop.schemeClr.shade.val = (int)updatedMods.shade * 1000;
                }
                if (updatedMods.hueMod != 100 || mods.hueMod != 100)
                {
                    gs.Stop.schemeClr.hueMod = new OfficeOpenXMLInterop.CT_PositivePercentage();
                    gs.Stop.schemeClr.hueMod.val = (int)updatedMods.hueMod * 1000;
                }
                if (updatedMods.satMod != 100 || mods.satMod != 100)
                {
                    gs.Stop.schemeClr.satMod = new OfficeOpenXMLInterop.CT_Percentage();
                    gs.Stop.schemeClr.satMod.val = (int)updatedMods.satMod * 1000;
                }
                if (updatedMods.lumMod != 100 || mods.lumMod != 100)
                {
                    gs.Stop.schemeClr.lumMod = new OfficeOpenXMLInterop.CT_Percentage();
                    gs.Stop.schemeClr.lumMod.val = (int)updatedMods.lumMod * 1000;
                }
                if (updatedMods.redMod != 100 || mods.redMod != 100)
                {
                    gs.Stop.schemeClr.redMod = new OfficeOpenXMLInterop.CT_Percentage();
                    gs.Stop.schemeClr.redMod.val = (int)updatedMods.redMod * 1000;
                }
                if (updatedMods.greenMod != 100 || mods.greenMod != 100)
                {
                    gs.Stop.schemeClr.greenMod = new OfficeOpenXMLInterop.CT_Percentage();
                    gs.Stop.schemeClr.greenMod.val = (int)updatedMods.greenMod * 1000;
                }
                if (updatedMods.blueMod != 100 || mods.blueMod != 100)
                {
                    gs.Stop.schemeClr.blueMod = new OfficeOpenXMLInterop.CT_Percentage();
                    gs.Stop.schemeClr.blueMod.val = (int)updatedMods.blueMod * 1000;
                }

                this.UpdateSelectedColor();
                this.UpdateGradientPreview();
            }
        }

        private void GradientEditor_VisibleChanged(object sender, EventArgs e)
        {
            if (this.Visible)
            {
                this.UpdateGradientPreview();
            }
        }

        private void panelGradientPreview_Paint(object sender, PaintEventArgs e)
        {
            if (this.comboBoxGradStops.Items.Count < 2)
                return;

            Graphics graphics = Graphics.FromHwnd(this.panelGradientPreview.Handle);

            RectangleF bounds = graphics.VisibleClipBounds;

            BufferedGraphicsContext currentContext;
            BufferedGraphics buffer;

            // Gets a reference to the current BufferedGraphicsContext
            currentContext = BufferedGraphicsManager.Current;

            // Creates a BufferedGraphics instance associated with our graphics
            buffer = currentContext.Allocate(graphics,
               new Rectangle((int)bounds.X, (int)bounds.Y, (int)bounds.Width, (int)bounds.Height));

            float width = bounds.Width;

            PointF start = new Point();
            PointF end = new Point();

            start.Y = 0f;
            end.Y = 0f;
            start.X = 0f;
            end.X = 0f;

            lock (this.comboBoxGradStops)
            {
                GradientStop gsStart = (GradientStop)this.comboBoxGradStops.Items[0];

                // fill in the background
                TextureBrush background = new TextureBrush(global::Theme_Builder.Properties.Resources.checker);
                buffer.Graphics.FillRectangle(background, 0f, 0f, bounds.Width, bounds.Height);

                for (int i = 1; i < this.comboBoxGradStops.Items.Count; i++)
                {
                    GradientStop gsEnd = (GradientStop)this.comboBoxGradStops.Items[i];

                    Color startColor = this.GetColorFromStop(gsStart);
                    Color endColor = this.GetColorFromStop(gsEnd);

                    start.X = ((float)gsStart.Stop.pos / 100000f) * width;
                    end.X = ((float)gsEnd.Stop.pos / 100000f) * width;

                    if (start.X < end.X)
                    {
                        LinearGradientBrush lgb = new LinearGradientBrush(start, end, startColor, endColor);
                        buffer.Graphics.FillRectangle(lgb, start.X, 0f, end.X - start.X, bounds.Height);
                    }

                    gsStart = gsEnd;
                }

                // Renders the contents of the buffer to the drawing surface associated 
                // with the buffer.
                buffer.Render();

                buffer.Dispose();
                graphics.Dispose();
            }
        }

        private void panelColor_Paint(object sender, PaintEventArgs e)
        {
            if (this.comboBoxGradStops.SelectedItem == null)
                return;

            lock (this.comboBoxGradStops)
            {
                Graphics g = Graphics.FromHwnd(this.panelColor.Handle);

                RectangleF bounds = g.VisibleClipBounds;

                Color color = this.GetColorFromStop((GradientStop)this.comboBoxGradStops.SelectedItem);

                SolidBrush sb = new SolidBrush(color);

                g.FillRectangle(sb, bounds);

                g.Dispose();
            }
        }
    }

    public class GradientStop
    {
        private int number;
        private OfficeOpenXMLInterop.CT_GradientStop stop;

        public GradientStop()
        {
            this.number = 1;
        }

        public int Number
        {
            get { return this.number; }
            set { this.number = value; }
        }

        public OfficeOpenXMLInterop.CT_GradientStop Stop
        {
            get { return this.stop; }
            set { this.stop = value; }
        }

        public override string ToString()
        {
            return "Stop " + this.number.ToString();
        }
    }

    /// <summary>
    /// This class helps sync message handling between two controls.  In this case between
    ///     a TrackBar and a NumericUpDown control.  This is needed because each control
    ///     fires events to one-another on value change which basically makes a circular
    ///     reference of constant messages that the controls values change.
    /// </summary>
    class ControlSyncTrackBarNumericUpDown
    {
        private Mutex mtx;
        private TrackBar trackBar;
        private NumericUpDown numericUpDown;

        /// <summary>
        /// Constructor for the sync class
        /// </summary>
        /// <param name="trackBarIn">TrackBar control to sync</param>
        /// <param name="numericUpDownIn">NumericUpDown control to sync</param>
        public ControlSyncTrackBarNumericUpDown(TrackBar trackBarIn, NumericUpDown numericUpDownIn)
        {
            this.mtx = new Mutex();

            this.trackBar = trackBarIn;
            this.numericUpDown = numericUpDownIn;
        }

        /// <summary>
        /// Changes the values contained in the two controls
        /// </summary>
        /// <param name="value">Value that each control should have</param>
        public void SyncControls(decimal value)
        {
            // Lock the CS
            mtx.WaitOne();

            // If the trackBar value is not the one that changed, then go ahead and update
            if (this.trackBar.Value != (int)value)
            {
                this.trackBar.Value = (int)value;
            }

            // If the numericUpDown value is not hte one that changed, then go ahead and update
            if (this.numericUpDown.Value != value)
            {
                this.numericUpDown.Value = value;
            }

            // Unlock the CS
            mtx.ReleaseMutex();
        }
    }
}